#include "DSqliteMgr.h"

#include <QSqlError>
#include <QSqlQuery>
DSqliteMgr::DSqliteMgr() {}

void DSqliteMgr::initDB()
{
    m_db = QSqlDatabase::addDatabase("QSQLITE", "DouDouConnection");
    m_db.setDatabaseName(QApplication::applicationDirPath() + "/Homework.db");
    if (!m_db.open())
    {
        qDebug() << "Error: Failed to connect database." << m_db.lastError();
        return;
    }
}

void DSqliteMgr::testDB()
{
    QDate start(2000, 1, 1);
    QDate end(2000, 12, 31);
    for (; start <= end; start = start.addDays(1))
    {
        for (int i = 1; i <= 46; ++i)
        {
            for (int j = 1; j <= 6; ++j)
            {
                const QString str =
                    QString(
                        "INSERT INTO "
                        "homework_list (student_id,homeworkType_id,"
                        "submit_date) VALUES (%1, %2, '%3')")
                        .arg(i)
                        .arg(j)
                        .arg(start.toString("yyyy-MM-dd"));
                execSQL(str);
            }
        }
    }
}

void DSqliteMgr::submit(const QDate& dt, IDHomeworkType type,
                        const std::vector<DAddHomework>& vecYes)
{
    qInfo() << 1;
    execSQL(QString("delete  FROM homework_list where submit_date = \'%1\' and "
                    "homeworkType_id=%2")
                .arg(dt.toString("yyyy-MM-dd"))
                .arg(type));

    for (auto& [_, v] : m_mapSubmit)
    {
        std::map<QDate, std::vector<DOneHomeworkSubmit>>& map1 = v;
        if (map1.find(dt) == map1.end())
        {
            continue;
        }

        std::vector<DOneHomeworkSubmit>& vec1 = map1[dt];
        auto itFind = std::find_if(vec1.begin(), vec1.end(),
                                   [type](const DOneHomeworkSubmit& s)
                                   { return s.nHomeworkTypeID == type; });

        if (itFind != vec1.end())
        {
            vec1.erase(itFind);
        }
    }

    qInfo() << 2;
    for (const auto& add : vecYes)
    {
        if (add.bYes)
        {
            const QString str = QString(
                                    "INSERT INTO "
                                    "homework_list (student_id,homeworkType_id,"
                                    "submit_date) VALUES (%1, %2, '%3')")
                                    .arg(add.student.nID)
                                    .arg(type)
                                    .arg(dt.toString("yyyy-MM-dd"));

            execSQL(str);
        }
    }
    qInfo() << 3;

    std::vector<QSqlRecord> vecInsert =
        execSQL(QString("select * from "
                        "homework_list where homeworkType_id=%1 and "
                        "submit_date = '%3'")
                    .arg(type)
                    .arg(dt.toString("yyyy-MM-dd")));

    for (const auto& r : vecInsert)
    {
        DOneHomeworkSubmit submit;
        submit.nID = r.value("id").toInt();
        submit.nStuentID = r.value("student_id").toString().toInt();
        submit.nHomeworkTypeID = r.value("homeworkType_id").toInt();
        QString s = r.value("submit_date").toString();
        submit.dtSubmit = QDate::fromString(s, "yyyy-MM-dd");
        m_mapSubmit[submit.nStuentID][submit.dtSubmit].push_back(submit);
    }

    qInfo() << 3.5;
}

std::vector<QSqlRecord> DSqliteMgr::execSQL(const QString& strSQL)
{
    // qInfo() << strSQL;

    std::vector<QSqlRecord> vecRet;

    QSqlQuery query(m_db);

    do
    {
        if (!query.exec(strSQL))
        {
            qDebug() << query.lastError().text();
            break;
        }

        while (query.next())
        {
            QSqlRecord r = query.record();
            vecRet.push_back(r);
        }
    } while (false);

    return vecRet;
}

std::vector<DHomeworkType> DSqliteMgr::getAllHomework()
{
    std::vector<DHomeworkType> vecRet;

    for (const auto& sub : m_vecSubject)
    {
        vecRet.insert(vecRet.begin(), sub.vecHomeworkType.begin(),
                      sub.vecHomeworkType.end());
    }

    return vecRet;
}

QString DSqliteMgr::getHomeworkTypeName(int nHomeworkTypeID)
{
    QString strRet;
    for (const auto& sub : m_vecSubject)
    {
        auto itFind =
            std::find_if(sub.vecHomeworkType.begin(), sub.vecHomeworkType.end(),
                         [nHomeworkTypeID](const DHomeworkType& t)
                         { return t.nID == nHomeworkTypeID; });

        if (itFind != sub.vecHomeworkType.end())
        {
            strRet = QString::fromStdString((*itFind).strName);
            break;
        }
    }

    return strRet;
}

std::vector<DUISubmit> DSqliteMgr::getSubmitYesNoData(IDStudent idStudent,
                                                      const QDate& dtS,
                                                      const QDate& dtE,
                                                      IDHomeworkType idType)
{
    std::vector<DUISubmit> vecRet;
    do
    {
        DObjectPtr<DSqliteMgr> sqliteMgr;

        std::map<QDate, std::vector<DOneHomeworkSubmit>>& mapDateSubmit =
            m_mapSubmit[idStudent];

        const QString strHomeworkName = sqliteMgr->getHomeworkTypeName(idType);

        // date
        for (QDate s = dtS; s <= dtE; s = s.addDays(1))
        {
            DUISubmit ui;
            ui.strHomeworkTypName = strHomeworkName;
            ui.dtSubmit = s;
            ui.bSubmit = false;
            auto it = mapDateSubmit.find(s);
            if (it != mapDateSubmit.end())
            {
                ui.bSubmit = std::any_of(
                    mapDateSubmit[s].begin(), mapDateSubmit[s].end(),
                    [idType](const DOneHomeworkSubmit& laS)
                    { return laS.nHomeworkTypeID == idType; });
            }

            vecRet.push_back(ui);
        }

    } while (false);

    return vecRet;
}

bool DSqliteMgr::getStudent(DStudent& student, int nID) const
{
    auto itFind =
        std::find_if(m_vecStudent.begin(), m_vecStudent.end(),
                     [nID](const DStudent& s) { return s.nID == nID; });

    const bool bRet = (itFind != m_vecStudent.end());

    if (bRet)
    {
        student = *itFind;
    }

    return bRet;
}
